# -*- coding: utf-8 -*-

import socket
import threading
import sys
import settings as cfg
import time
import random
import string
import lang
from datetime import datetime
import MySQLdb
import copy

class Sock:
	current_word = ""
	current_sec_word = ""
	current_description = ""
	hidden_word = ""
	current_cost = 0
	current_hint = cfg.MAX_HINTS
	srv = None
	activeConnections = []
	activeClients = {}
	stopped = False
	time_diff = 0
	start_time = 0
	finish_time = 0
	paused = False
	is_anagram = False
	questions = []
	conn = None
	cursor = None
	
	def __init__(self):
		reload(sys)
		sys.setdefaultencoding('utf-8')
		self.conn = MySQLdb.connect (host=cfg.GAME_DB_HOST,user=cfg.GAME_DB_USER,passwd=cfg.GAME_DB_PASSWD,db=cfg.GAME_DB, charset = "utf8", use_unicode = True)
		self.cursor = self.conn.cursor()
		
		self.srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		self.srv.bind((cfg.SOCK_HOST, cfg.SOCK_PORT))
		self.loop()

		
	def proc(self, curr_sock):	
	
		self.activeConnections.append(curr_sock)
		#print self.activeConnections
		while(1):
			data = curr_sock.recv(1024)

			if not data:
				if curr_sock in self.activeClients:
					del self.activeClients[curr_sock]
					curr_sock.close()
					#self.srv.close()
				break

			
			data = string.replace(data, '\r', '')
			data = string.replace(data, '\r', '')
			data = string.replace(data, '\0', '')

			data = data[0:300]
			#print data
			
			if data == '<policy-file-request/>':
				police_file = '<?xml version="1.0" encoding="UTF-8"?><cross-domain-policy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd"><allow-access-from domain="*" to-ports="*" secure="false" /></cross-domain-policy>'
				curr_sock.send(police_file)
				#print 'sending request'
				curr_sock.close()
				return
				break
			if not curr_sock in self.activeClients:
				tmp_data = data.split("|")
				#to do AUTH + get points
				if len(tmp_data) > 1 and tmp_data[0] == "login":
				
					try:
						id=int(tmp_data[2])
					except:
						break
						
					self.cursor.execute("SELECT points,answers,best_time,id,banned FROM users WHERE ID=" + str(id) )
					user = self.cursor.fetchall()[0]
					if user[4]:
						self.send_client(lang.get_phrase('you_banned'), curr_sock)
						curr_sock.send(police_file)
						return
						
					if user:
						self.activeClients[curr_sock] = {'login':tmp_data[1], 'points':int(user[0]), 'total_answers':int(user[1]), 'best_time':float(user[2]), "id":int(user[3])} 
					else:
						self.activeClients[curr_sock] = {'login':tmp_data[1], 'points':0, 'total_answers':0, 'best_time':0, "id":id} 
					

					
					self.send_client(lang.get_phrase('hi') + tmp_data[1] +"! <br/>" + lang.get_phrase('help'), curr_sock)
					if self.current_word != "":
						self.send_client(lang.get_phrase('curr_qst') + self.get_total_question(), curr_sock)
					self.chat(tmp_data[1] + lang.get_phrase('joined'), curr_sock)
					continue
			
			
			if not curr_sock in self.activeClients:
				continue
				
			if "!ping" in data:
				continue
				
			if data == "!a" or data == "!ф":
				self.send_client(self.current_description, curr_sock)
				continue
			
			if data == "!p" or data == "!з":
				self.send_client(
									lang.get_phrase('your_points') + 
									str(self.activeClients[curr_sock]['points']) + 
									lang.get_phrase('total_answers') + 
									str(self.activeClients[curr_sock]['total_answers']) + 
									lang.get_phrase('best_time') + 
									str(self.activeClients[curr_sock]['best_time']), 
									curr_sock
								)
				continue
				
			if data == "!help" or data == "!рудз" or data == "![tkg" or data == "!хелп" :
				self.send_client(lang.get_phrase('help'), curr_sock)
				continue
			
			self.send(data, self.activeClients[curr_sock]['login'])
			
			if self.current_word.decode("utf-8").lower().encode("utf-8") in data.decode("utf-8").lower().encode("utf-8") or self.current_sec_word in data.decode("utf-8").lower().encode("utf-8") and not self.paused:
				self.paused = True
				self.activeClients[curr_sock]['points'] = self.activeClients[curr_sock]['points'] + self.current_cost
				curr_time = round(time.time() - self.start_time, 2)
				if curr_time < self.activeClients[curr_sock]['best_time'] or self.activeClients[curr_sock]['best_time'] == 0:
					self.activeClients[curr_sock]['best_time'] = curr_time
				self.activeClients[curr_sock]['total_answers'] = self.activeClients[curr_sock]['total_answers'] + 1
				self.send(
							self.activeClients[curr_sock]['login'] + 
							lang.get_phrase('bingo') + 
							"<span class='answer'>" + 
							self.current_word.decode("utf-8").encode("utf-8") + "</span>" + 
							lang.get_phrase('answer_time') + str(curr_time) + lang.get_phrase('seconds') + 
							lang.get_phrase('award') + str(self.current_cost) + 
							lang.get_phrase('total_points') + str(self.activeClients[curr_sock]['points']) + 
							lang.get_phrase('total_answers') + str(self.activeClients[curr_sock]['total_answers']) + 
							lang.get_phrase('best_time') + str(self.activeClients[curr_sock]['best_time'])
						)
				self.current_word = ""
				time.sleep(1)
				self.new_question()
			if data == "!h" or data == "!р":
				if (cfg.MAX_HINTS - self.current_hint ) * cfg.HIT_MIN_TIME + cfg.HIT_MIN_TIME <= self.time_diff:
					self.get_hint()

			
			

		
	def new_question(self):
		#to do get answers from DB
		if len(self.questions) < 50:
			ids = ""
			self.cursor.execute("SELECT question,answer,cost,id FROM questions ORDER BY viewed LIMIT 100")
			for tmp_q in self.cursor.fetchall():
				q = {}
				q['q'] 	= tmp_q[0]
				q['a'] 	= tmp_q[1]
				q['c'] 	= tmp_q[2]
				q['id'] = tmp_q[3]
				self.questions.append(q)
				ids = ids + str(tmp_q[3]) + ","
			ids = ids[0:-1]
			self.cursor.execute("UPDATE questions SET viewed=viewed+1 WHERE id IN (" + ids + ")")
			
		curr_item = random.choice(self.questions)
		
		#print 'answer', curr_item['a']
		curr_item['a'] = curr_item['a']
		self.current_word = curr_item['a']
		self.current_sec_word = lang.get_sec_word(self.current_word)
		r = random.randint(0,99)
		 #anagram
		if r < cfg.ANAGRAM_PERCENT:
			self.current_cost = curr_item['c'] + random.randint(len(self.hidden_word),len(self.hidden_word)*2) 
			cw = list(self.current_word)
			random.shuffle(cw)
			self.hidden_word = ''.join(cw).lower()
			self.current_description = lang.get_phrase('anagram')
			self.is_anagram = True
		# * in description
		elif r > cfg.ANAGRAM_PERCENT and r <= cfg.HIDE_DESCR_PERCENT + cfg.ANAGRAM_PERCENT: 
			cr = random.randint(4, 6)
			self.current_cost = curr_item['c'] * (9 - cr)
			hd = list(curr_item['q'])
			for i,k in enumerate(hd[0:-1:cr]):
				if k == " ":
					continue
				else:
					hd[i*cr] = "*"
			self.current_description = ''.join(hd)
			
			self.hidden_word = curr_item['a'][0:2] + "*" * (len(curr_item['a'])-2)
		#shuffle letters in description
		elif r > ( cfg.HIDE_DESCR_PERCENT + cfg.ANAGRAM_PERCENT ) and r <= ( cfg.HIDE_DESCR_PERCENT + cfg.ANAGRAM_PERCENT + cfg.SHUFFLE_DESCR_PERCENT ) : #
			words = curr_item['q'].split(" ")
			for i, k in enumerate(words):
				w = list(k)
				if (len(w)) > 2 and not "." in w and not "?" in w and not "'" in w and not '"' in w and not "!" in w:
					f = w[0]
					l = w[len(w)-1]
					del w[0]
					del w[len(w)-1]
					random.shuffle(w)
					w.insert(0,f)
					w.insert(len(w), l)
				words[i] = ''.join(w)
			
			self.current_cost = curr_item['c'] * len(words)
			self.current_description = ' '.join(words)
			self.hidden_word = curr_item['a'][0:2] + "*" * (len(curr_item['a'])-2)
		#shuffle words in description
		elif r > ( cfg.HIDE_DESCR_PERCENT + cfg.ANAGRAM_PERCENT + cfg.SHUFFLE_DESCR_PERCENT ) and r <= ( cfg.HIDE_DESCR_PERCENT + cfg.ANAGRAM_PERCENT + cfg.SHUFFLE_DESCR_PERCENT + cfg.SHUFFLE_WORDS_DESCR_PERCENT ):
			words = curr_item['q'].split(" ")
			self.current_cost = curr_item['c'] * len(words)
			if(len(words) > 1):
				f = words[0]
				l = words[len(words)-1]
				del words[0]
				del words[len(words)-1]
				random.shuffle(words)
				words.insert(0,f)
				words.insert(len(words), l)
				self.hidden_word = curr_item['a'][0:2] + "*" * (len(curr_item['a'])-2)
			else:
				self.hidden_word = "*" * len(curr_item['a'])
			self.current_description = ' '.join(words)
			
		#normal question
		else:
			self.hidden_word = "*" * len(curr_item['a'])
			self.current_cost = curr_item['c']
			self.current_description = curr_item['q']
			self.is_anagram = False
			
		del self.questions[self.questions.index(curr_item)]
		self.send(self.get_total_question())
		self.current_hint = cfg.MAX_HINTS
		self.start_time = time.time()
		self.paused = False
	
	def get_hint(self):
		if self.is_anagram:
			return
			
		if self.current_hint > 0:
			if len(self.current_word) > cfg.MIN_LETTERS_HINT:
				while 1:
					i = random.randint(0, len(self.current_word) -1)
					cw = list(self.current_word)
					hw = list(self.hidden_word)
					
					if hw[i] == "*":
						hw[i] = cw[i]
						self.hidden_word = "".join(hw)
						self.send(self.hidden_word)
						self.current_hint = self.current_hint-1
						if self.current_cost > 0:
							self.current_cost = self.current_cost-1
						break
	
	#send all
	def send(self, msg, sender=None):
		#print self.activeClients
		for client in self.activeClients:
			#print 'send to:', client
			self.send_client(msg, client, sender),
	#send others
	def chat(self, msg, curr_sock, sender=None):
		for client in self.activeClients:
			if client != curr_sock:
				self.send_client(msg, client,sender)
	#send private
	def send_client(self, msg, curr_sock, sender=None):
		username_class = "username"
		if not sender:
			sender = lang.get_phrase('bot_name')
			username_class = "botname"
			
		t = datetime.now()
		curr_sock.send("<p>" + "<span class='time'>%02d:%02d:%02d</span> "%(t.hour, t.minute, t.second) + "<span class='"+ username_class  +"'>" + sender + "</span>: " + msg + '</p>\0')
		
	
	def get_total_question(self):
		return self.current_description + " (" + "<span class='letters_num'>" + str(len(self.current_word)) + "</span>" +  lang.get_phrase('letters_num') +")" + lang.get_phrase('cost') + str(self.current_cost) + "\n" + "<span class='hint'>" + self.hidden_word + "</span>"
	
	def cnt(self):
		print 'Server started'
		
		while(1):
			if self.stopped:
				break
			if self.paused:
				continue
			if self.time_diff > cfg.QUESTIONS_INTERVAL:
				self.paused = True
				self.send("<span class='nobody_won'>" + lang.get_phrase('nobody_won') + "</span><span class='answer'>" + self.current_word + "</span>")
				time.sleep(1)
				self.new_question()
				
			if int((cfg.QUESTIONS_INTERVAL*100)*cfg.FIRST_WARN_PERCENT/10000) == self.time_diff:
				self.send(lang.get_phrase('first_warn') + str(int(cfg.QUESTIONS_INTERVAL - self.time_diff)) + lang.get_phrase('seconds'))
				self.get_hint()
				
			if int((cfg.QUESTIONS_INTERVAL*100)*cfg.SECOND_WARN_PERCENT/10000) == self.time_diff:
				self.send(lang.get_phrase('second_warn') + str(int(cfg.QUESTIONS_INTERVAL - self.time_diff)) + lang.get_phrase('seconds'))
				self.get_hint()

			#finish_time = time.time()
			self.time_diff = int(time.time() - self.start_time)
			time.sleep(1)

	def save(self):
		while(1):
			if self.stopped:
				break
			to_save = copy.deepcopy(self.activeClients)
			for c in to_save:
				client = to_save[c]
				self.cursor.execute(
								"UPDATE users SET points=" + str(client['points']) 
								+ ", answers=" + str(client['total_answers']) 
								+ ", best_time=" + str(client['best_time'])
								+ " WHERE ID=" + str(client['id']) 
							)
				time.sleep(5)
				
				
	def loop(self):
		thr = threading.Thread(target=self.cnt)
		thr.start()
		thr = threading.Thread(target=self.save)
		thr.start()
		self.new_question()
		while(1):
			try:
				self.srv.listen(1)
				curr_sock, self.addr = self.srv.accept()
				thr = threading.Thread(target=self.proc, args=[curr_sock])
				thr.start()
				
			except(KeyboardInterrupt):
				print "\nServer stopped"
				self.stopped = True
				self.srv.close()
				sys.exit(0)
				break;
	
Sock()
